// Copyright (c) 2009, Google Inc.
// Original file from http://code.google.com/p/secrets-for-android/
// Modifications (c) 2010, Patrick Dubroy <pdubroy@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.dubroy.mnemobot;

import android.graphics.Camera;
import android.graphics.Matrix;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * Performs a flip animation between two views. This implementation is highly
 * inspired by the 3D transition sample in the android SDK, but corrects a huge
 * bug where the destination view remains flipped 180 degree! This is not
 * apparent when the destination is a simple image. Also, this class is more
 * easily reusable, but less customizable by the caller.
 * 
 * The following restrictions apply:
 * <ul>
 * <li>The view that this animation is applied to must be the parent of the two
 * views specified in the constructor
 * <li>The two views specified in the constructor should be siblings, and direct
 * children of the view to which the animation is applied
 * </ul>
 * 
 * The animation is a rotation of the first view, until it reaches 90 degrees,
 * at which point the rotation continues but with the second view. The views
 * also move backwards slightly and them come forward into position again. The
 * net effect is as if you flipped a sheet of paper, where each side shows one
 * of the views. Its a very similar effect to editing the properties of a Mac
 * dashboard widget.
 * 
 * @author rogerta
 */
public class Flip3dAnimation extends Animation {
	private Camera camera;
	private View fromView;
	private View toView;
	private float centerX;
	private float centerY;
	private boolean forward = true;

	private boolean visibilitySwapped;
	
	/**
	 * Creates a 3D flip animation between two views. If forward is true, its
	 * assumed that view1 is "visible" and view2 is "gone" before the animation
	 * starts. At the end of the animation, view1 will be "gone" and view2 will
	 * be "visible". If forward is false, the reverse is assumed.
	 * 
	 * @param fromView
	 *            First view in the transition.
	 * @param toView
	 *            Second view in the transition.
	 * @param centerX
	 *            The center of the views in the x-axis.
	 * @param centerY
	 *            The center of the views in the y-axis.
	 * @param forward
	 *            The direction of the animation.
	 */
	Flip3dAnimation(View fromView, View toView, int centerX, int centerY) {
		this.fromView = fromView;
		this.toView = toView;
		this.centerX = centerX;
		this.centerY = centerY;

		setDuration(500);
		setFillAfter(true);
		setInterpolator(new AccelerateDecelerateInterpolator());
	}
	
	public void reverse() {
		forward = false;
		View temp = toView;
		toView = fromView;
		fromView = temp;
	}

	@Override
	public void initialize(int width, int height, int parentWidth,
			int parentHeight) {
		super.initialize(width, height, parentWidth, parentHeight);
		camera = new Camera();
	}

	@Override
	protected void applyTransformation(float interpolatedTime, Transformation t) {
		// Angle around the y-axis of the rotation at the given time. It is
		// calculated both in radians and in the equivalent degrees.
		final double radians = Math.PI * interpolatedTime;
		float degrees = (float) (180.0 * radians / Math.PI);

		// Once we reach the midpoint in the animation, we need to hide the
		// source view and show the destination view. We also need to change
		// the angle by 180 degrees so that the destination does not come in
		// flipped around. This is the main problem with SDK sample, it does not
		// do this.
		if (interpolatedTime >= 0.5f) {
			degrees -= 180.f;

			if (!visibilitySwapped) {
				fromView.setVisibility(View.GONE);
				toView.setVisibility(View.VISIBLE);

				visibilitySwapped = true;
			}
		}

		if (forward)
			degrees = -degrees;

		final Matrix matrix = t.getMatrix();

		camera.save();
		camera.translate(0.0f, 0.0f, (float) (150.0 * Math.sin(radians)));
		camera.rotateY(degrees);
		camera.getMatrix(matrix);
		camera.restore();

		matrix.preTranslate(-centerX, -centerY);
		matrix.postTranslate(centerX, centerY);
	}
}